#pragma once

#include <stdio.h>
#include <string>
#include <direct.h>

/**
 * @file ia.h
 *
 * @brief Common literals, macros and structures defination
 * 
 * Naming rules:
 *				IA_		Intelligent Automation
 *				IA_L_		literals
 *				IA_E_		enumerations
  *				IA_F_		functions
 *
 * @author cygia.com
 * Contact: lance.liao@cygia.com
 *
 */

#define IA_L_TRUE					1
#define IA_L_FALSE					0

#define IA_L_OK					1
#define IA_L_ERR					0

#define IA_RETURN_TRUE			return(IA_L_TRUE);
#define IA_RETURN_FALSE		return(IA_L_FALSE);

enum class IA_E_LOG_LEVEL { IA_L_DEBUG, IA_L_INFO, IA_L_WARN, IA_L_ERROR, IA_L_FATAL, IA_L_MSGBOX};

typedef void (*IA_F_LOG_CALLBACK)(IA_E_LOG_LEVEL level, const char* file, const char* func, int line, char* msg, void* pUser);
extern void ia_log_set_callback(IA_F_LOG_CALLBACK* pcallBack, void* pUser);
extern void ia_log(IA_E_LOG_LEVEL level, char* msg, const char* file, const char* func, int line);
#define IA_LOG_PROXY(x,...) { char buf[512]; _snprintf_s(buf, _TRUNCATE, __VA_ARGS__);  ia_log(x, buf, __FILE__, __func__ , __LINE__); }

#define IA_LOG_SET_CALLBACK(x,u)		ia_log_set_callback(x,u);
#define IA_DEBUG(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_DEBUG,	__VA_ARGS__);
#define IA_INFO(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_INFO,		__VA_ARGS__);
#define IA_WARN(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_WARN,		__VA_ARGS__);
#define IA_ERROR(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_ERROR,	__VA_ARGS__);
#define IA_FATAL(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_FATAL,	__VA_ARGS__);
#define IA_MSGBOX(...)		IA_LOG_PROXY(IA_E_LOG_LEVEL::IA_L_MSGBOX,	__VA_ARGS__);

#define IA_RETURN_OK		return(IA_L_OK);
#define IA_RETURN_ERR		{ IA_ERROR("function %s return error!", __func__);  return(IA_L_ERR); }
#define STATUS_CHECK		{ if (status != IA_L_OK) { IA_ERROR("function %s error with status code=%d", __func__,  status); return(status); }}
#define STATUS_CHECK_MSG(...) {if(status != IA_L_OK) { IA_ERROR(__VA_ARGS__); return status;}}
#define STATUS_CHECK_POPUP(...) {if(status != IA_L_OK) {IA_ERROR(__VA_ARGS__); IA_MSGBOX(__VA_ARGS__); return status;}}

#define IA_ASSERT(x) if (!(x)) { IA_ERROR("assert (" #x ") failed!"); return(IA_L_ERR); }
#define IA_ASSERT_MSG(x, ...) {if (!(x)) { IA_ERROR("assert (" #x ") failed! "); IA_ERROR(__VA_ARGS__); return(IA_L_ERR); }}
#define IA_ASSERT_POPUP(x, ...) {if (!(x)) { IA_ERROR("assert (" #x ") failed! "); IA_ERROR(__VA_ARGS__); IA_MSGBOX(__VA_ARGS__); return(IA_L_ERR); }}
#define IA_TRACE(...) {  IA_INFO(__VA_ARGS__); }

#define IA_MALLOC(p, type, old_len, new_len) { \
	if( NULL == p || old_len < new_len) { \
		if(p != NULL)  IA_FREE(p); \
		if(new_len ==0) IA_FATAL("Invalid malloc size 0"); \
		p = (type)malloc(new_len); \
		if(p == NULL) IA_FATAL("malloc failed, system out of memory!"); \
		memset(p, 0, new_len); \
	} \
}

#define IA_FREE(x) { \
	if(NULL != x) { \
		free(x); \
	} \
	x = NULL; \
}

#define IA_DELETE(x) { \
	if(NULL != x) { \
		delete x; \
	} \
	x = NULL; \
}

#define IA_DELETE_ARRAY(x) {\
	if(NULL != x) {\
		delete[] x; \
	}\
	x = NULL; \
}

// Check format specifiers at https://github.com/amrayn/easyloggingpp
extern std::string ia_format_datetime(const char* format, int precision);
#define IA_FORMAT_DATETIME(x, ms)  ia_format_datetime(x, ms)
#define IA_FORMAT_DATE_TODAY  IA_FORMAT_DATETIME("%Y-%M-%d", 3)
#define IA_FORMAT_DATETIME_NOW_MS  IA_FORMAT_DATETIME("%Y-%M-%d_%H-%m-%s-%g", 3)
#define IA_FORMAT_DATETIME_NOW_MS_NEW  IA_FORMAT_DATETIME("%Y-%M-%d %H:%m:%s", 3)

extern int ia_make_dir(std::string& dir);
#define IA_MAKE_DIR(dir) ia_make_dir(dir);

// TODO
// Multiple language support
#define _(x)		x